package com.cn.encryp;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @description: RSA非对称加密算法
 * @Author 朱明川
 *
 * todo SHA-256算法特点：
 * 不可逆性：无法通过散列值反向推导出原始数据;
 * 高强度：对于相同的输入，即使改变一个字符也会得到完全不同的散列值;
 * 哈希冲突率低：即使SHA-256输出的256位二进制值空间庞大，但仍有可能出现不同的输入得到相同的散列值，这种情况称为哈希冲突。
 * todo 注意:
 * SHA-256本身是一个哈希函数，它并不加密数据，而是生成数据的哈希值（或称消息摘要）。这个哈希值是一个复杂的数字指纹，对于任何给定的输入，它几乎是唯一的。这意味着，即使输入数据只有微小的变化，其哈希值也会发生显著变化。
 * SHA-256生成的哈希值是一个固定长度的值，具体为256位（32字节）的二进制数据。然而，在数据库中存储时，我们通常会将这些二进制数据转换为十六进制（0-9, A-F）字符串表示，以便阅读和查询。因此256位二进制数据将转换为64个字符的十六进制字符串来存储VARCHAR(64)。
 * SHA-256算法的主要步骤如下：
 * 初始化一个256位的信息摘要。
 * 处理数据块。将数据分成512位的数据块，如果不足512位，则通过填充规则补足。
 * 最终摘要。将填充的数据块通过一系列的位操作转换为一个新的信息摘要。
 */

public class SHA256 {


    /**
     * 使用SHA-256算法计算多个对象（字符串）的联合哈希值
     *
     * @param inputs 输入的对象数组
     * @return 十六进制的哈希值
     */
    public static String getSHA256Hex(Object... inputs) {
        try {
            // 创建MessageDigest实例，初始化为SHA-256算法
            MessageDigest md = MessageDigest.getInstance("SHA-256");

            // 遍历所有输入字符串，将它们添加到哈希计算中
            for (Object input : inputs) {
                // 如果为空，则设置为null字符串
                String inputStr = input== null ? "null" : input.toString();
                md.update(inputStr.getBytes(StandardCharsets.UTF_8));
            }

            // 计算哈希值
            byte[] hash = md.digest();

            // 将字节数组转换为十六进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 验证数据的哈希值是否匹配
     *
     * @param storedHash 存储的哈希值
     * @param inputs     要验证的输入对象数组
     * @return 如果哈希值匹配，则返回true；否则返回false
     */
    public static boolean verifyHash(String storedHash, Object... inputs) {
        String computedHash = getSHA256Hex(inputs);
        return computedHash.equals(storedHash);
    }

    public static void main(String[] args) {
        String[] inputs = {"Hello, ", "SHA-256!", " Multiple", " inputs", null};
        String hash = getSHA256Hex(inputs);
        System.out.println("Combined Hash : " + hash);
        System.out.println("Combined Hash1: " + getSHA256Hex("a1"));
        System.out.println("Combined Hash2: " + getSHA256Hex("a1", "a2"));

        // 假设这是之前存储的哈希值
        String storedHash = hash; // 在实际应用中，这将是从某处检索的

        // 验证哈希值
        boolean isVerified = verifyHash(storedHash, inputs);
        System.out.println("Data Verified: " + isVerified);

        String[] inputs2 = {"Hello, ", "SHA-256!", " Multiple", " inputs"};
        boolean isVerified2 = verifyHash(storedHash, inputs2);
        System.out.println("Data Verified2: " + isVerified2);

        // 修改输入后尝试验证
        String[] modifiedInputs = {"Hello, ", "SHA-256?!", " Modified", " inputs"};
        boolean isModifiedVerified = verifyHash(storedHash, modifiedInputs);
        System.out.println("Modified Data Verified: " + isModifiedVerified);
        System.out.println("Modified Data Verified2: " + verifyHash(null, modifiedInputs));
    }
}
